
import java.util.*;
import ast.*;

/* pretty-printer (using the visitor) */

class Pretty extends EmptyVisitor {

	private static void print(String s) {
		System.out.print(s);
	}

	private static void println() {
		System.out.println();
	}

	private static void println(String s) {
		System.out.println(s);
	}

	private static int indent = 0;

	private static void newline() {
		println();
		for (int i = 0; i < indent; i++)
			print(" ");
	}

	public void visit(JmmFile n) {
		for (Iterator it = n.classes.iterator(); it.hasNext();)
			((JmmClass) it.next()).accept(this);
		n.main.accept(this);
	}

	public void visit(JmmClass n) {
		print("class " + n.class_id);
		if (!n.class_inh.id_val.equals("Object"))
			print(" inherits " + n.class_inh.id_val);
		print(" begin");
		indent = 2;
		newline();
		for (Iterator it = n.class_fields.iterator(); it.hasNext();)
			((Field) it.next()).accept(this);
		println("end");
	}

	public void visit(Fattribute n) {
		print(n.attr_ty.ty_val + " ");
		for (Iterator it = n.attr_ids.iterator(); it.hasNext();)
			print(((Ident) it.next()).id_val + ", ");
		print(";");
		newline();
	}

	static void formals(VarList l) {
		for (Iterator it = l.iterator(); it.hasNext();) {
			Var v = (Var) it.next();
			print(v.var_ty.ty_val + " " + v.var_id.id_val + ", ");
		}
	}

	public void visit(Fconstructor n) {
		print(n.cstr_id.id_val + "(");
		formals(n.cstr_args);
		print(")");
		indent += 2;
		newline();
		n.cstr_body.accept(this);
		indent = 2;
		newline();
	}

	public void visit(Fmethod n) {
		print(n.meth_ty.ty_val + " " + n.meth_id.id_val + "(");
		formals(n.meth_args);
		print(")");
		indent += 2;
		newline();
		n.meth_body.accept(this);
		indent = 2;
		newline();
	}

	public void visit(Ethis n) {
		print("this");
	}

	public void visit(Enull n) {
		print("null");
	}

	public void visit(Econst_bool n) {
		print(String.valueOf(n.val));
	}

	public void visit(Econst_int n) {
		print(String.valueOf(n.val));
	}

	public void visit(Econst_string n) {
		print(n.val);
	}

	public void visit(Eident n) {
		print(n.val.id_val);
	}

	void binop(Expr e1, String op, Expr e2) {
		print("(");
		e1.accept(this);
		print(op);
		e2.accept(this);
		print(")");
	}

	public void visit(Eplus n) {
		binop(n.e1, "+", n.e2);
	}

	public void visit(Eminus n) {
		binop(n.e1, "-", n.e2);
	}

	public void visit(Emult n) {
		binop(n.e1, "*", n.e2);
	}

	public void visit(Ediv n) {
		binop(n.e1, "/", n.e2);
	}

	public void visit(Elt n) {
		binop(n.e1, "<", n.e2);
	}

	public void visit(Ele n) {
		binop(n.e1, "<=", n.e2);
	}

	public void visit(Egt n) {
		binop(n.e1, ">", n.e2);
	}

	public void visit(Ege n) {
		binop(n.e1, ">=", n.e2);
	}

	public void visit(Eeq n) {
		binop(n.e1, "=", n.e2);
	}

	public void visit(Eneq n) {
		binop(n.e1, "<>", n.e2);
	}

	public void visit(Eand n) {
		binop(n.e1, "and", n.e2);
	}

	public void visit(Eor n) {
		binop(n.e1, "or", n.e2);
	}

	public void visit(Eassign n) {
		binop(n.e1, ":=", n.e2);
	}

	public void visit(Eseq n) {
		n.e1.accept(this);
		print(";");
		newline();
		n.e2.accept(this);
	}

	public void visit(Eneg n) {
		print("(-");
		n.e1.accept(this);
		print(")");
	}

	public void visit(Enot n) {
		print("(not ");
		n.e1.accept(this);
		print(")");
	}

	void args(ExprList l) {
		for (Iterator it = l.iterator(); it.hasNext();) {
			((Expr) it.next()).accept(this);
			print(", ");
		}
	}

	public void visit(Enew n) {
		print("(new " + n.f + "(");
		args(n.args);
		print("))");
	}

	public void visit(Eprimcall n) {
		print(n.f + "(");
		args(n.args);
		print(")");
	}

	public void visit(Efield n) {
		print("(");
		n.e.accept(this);
		print("." + n.f + ")");
	}

	public void visit(Emethcall n) {
		print("(");
		n.e.accept(this);
		print("." + n.f + "(");
		args(n.args);
		print("))");
	}

	public void visit(Eif n) {
		print("(if ");
		n.e1.accept(this);
		print(" then");
		indent += 2;
		newline();
		n.e2.accept(this);
		indent -= 2;
		newline();
		print(" else");
		indent += 2;
		newline();
		n.e3.accept(this);
		print(")");
		indent -= 2;
	}

	public void visit(Ewhile n) {
		print("(while ");
		n.e1.accept(this);
		print(" do");
		indent += 2;
		newline();
		n.e2.accept(this);
		print(")");
		indent -= 2;
	}

	public void visit(Eletin n) {
		print("let " + n.id + " = ");
		n.e1.accept(this);
		print(" in");
		newline();
		n.e2.accept(this);
	}

	public void visit(Enop n) {
		print("nop");
	}

}
